package com.own.component.common.base.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.pagehelper.PageInfo;
import com.own.aop.ServiceInfo;
import com.own.base.BaseResponseCode;
import com.own.component.common.base.BusinessErrorResponseCode;
import com.own.component.common.base.entity.BaseBo;
import com.own.component.common.base.entity.BaseMap;
import com.own.component.common.base.entity.BaseQuery;
import com.own.component.common.base.entity.po.BaseBean;
import com.own.component.common.base.entity.vo.BaseBeanVo;
import com.own.component.common.base.mapper.BasePageMapper;
import com.own.component.common.base.service.BaseService;
import com.own.component.common.base.service.hook.AbstractBaseHook;
import com.own.component.common.em.BusinessEnum;
import com.own.component.common.exception.BusinessSimpleException;
import com.own.component.common.model.PageModel;
import com.own.component.common.model.UpdateModel;
import com.own.component.common.util.MapperUtil;
import com.own.component.common.util.function.LambdaQueryFunction;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * AbstractBaseService
 *
 * @author chenxueli
 * @date 2022-06-15 03:14:15
 */
@Slf4j
@Service
public abstract class AbstractBaseService<
        PO extends BaseBean,
        BO extends BaseBo<PO>,
        VO extends BaseBeanVo<PO>,
        MAP extends BaseMap<PO>,
        QUERY extends BaseQuery,
        MAPPER extends BasePageMapper<PO, VO, MAP, QUERY>
        >
        extends AbstractBaseHook<PO, VO, MAP, QUERY>
        implements BaseService<PO, BO, VO, MAP, QUERY> {

    /**
     * 根据id查询异常的map
     */
    private static final Map<String, BaseResponseCode> ERROR_GET_BY_ID_MESSAGE_MAP = new HashMap<>();
    @Resource
    public MapperUtil mapperUtil;
    @Getter
    @Autowired
    @SuppressWarnings("all")
    private MAPPER mapper;

    /**
     * 查询所有的数据
     *
     * @param query 查询条件
     * @return 所有的列表数据
     */
    @Override
    public List<VO> all(QUERY query) {
        if (query == null) {
            afterList(listByWrapper((LambdaQueryWrapper<PO>) null).stream().map(this::ofVo).toList());
        }
        return afterList(listByWrapper(lambdaQueryWrapper(query)).stream().map(this::ofVo).toList());
    }

    /**
     * 分页查询信息
     *
     * @param query 查询条件
     * @param ids   是否需要查询id
     * @return 分页数据
     */
    @Override
    public PageModel<VO> page(QUERY query, boolean ids) {
        var page = query.page();
        if (ids) {
            var idList = mapper.listIds(query);
            if (idList.isEmpty()) {
                return new PageModel<>(new ArrayList<>(), PageInfo.of(idList));
            }
            query.setIdList(idList);
            return new PageModel<>(afterList(mapper.listByIds(query)), PageInfo.of(idList));
        } else {
            return new PageModel<>(all(query), page);
        }
    }

    /**
     * 查询map信息
     *
     * @param query 查询条件
     * @return 查询出的列表信息
     */
    @Override
    public List<MAP> map(QUERY query) {
        return listByWrapper(lambdaQueryWrapper(query)).stream().map(this::ofMap).toList();
    }

    /**
     * 根据查询wrapper查询一条数据
     *
     * @param wrapper 查询条件
     * @return 查询出的列表信息
     */
    @Override
    public PO getOneByWrapper(QueryWrapper<PO> wrapper) {
        return mapper.selectOne(wrapper);
    }

    /**
     * 根据查询wrapper查询一条数据
     *
     * @param wrapper 查询条件
     * @return 查询出的列表信息
     */
    @Override
    public PO getOneByWrapper(LambdaQueryWrapper<PO> wrapper) {
        return mapper.selectOne(wrapper);
    }

    /**
     * 根据查询wrapper查询数据列表
     *
     * @param wrapper 查询条件
     * @return 查询出的列表信息
     */
    @Override
    public List<PO> listByWrapper(QueryWrapper<PO> wrapper) {
        return mapper.selectList(wrapper);
    }

    /**
     * 根据查询wrapper查询数据列表
     *
     * @param wrapper 查询条件
     * @return 查询出的列表信息
     */
    @Override
    public List<PO> listByWrapper(LambdaQueryWrapper<PO> wrapper) {
        return mapper.selectList(wrapper);
    }

    /**
     * 根据查询wrapper查询数据数量
     *
     * @param wrapper 查询条件
     * @return 查询出的列表信息
     */
    @Override
    public Long countByWrapper(QueryWrapper<PO> wrapper) {
        return mapper.selectCount(wrapper);
    }

    /**
     * 根据查询wrapper查询数据数量
     *
     * @param wrapper 查询条件
     * @return 查询出的列表信息
     */
    @Override
    public Long countByWrapper(LambdaQueryWrapper<PO> wrapper) {
        return mapper.selectCount(wrapper);
    }

    /**
     * 查询相等的列表数据数量
     *
     * @param keyExtractor 字段信息
     * @param value        值
     * @return 数量
     */
    @Override
    public <R> Long countEqual(SFunction<PO, R> keyExtractor, R value) {
        var wrapper = lambdaQueryWrapper().eq(keyExtractor, value);
        return mapper.selectCount(wrapper);
    }

    /**
     * 查询相等的列表数据
     *
     * @param keyExtractor 字段信息
     * @param value        值
     * @param <R>          类型
     * @return 列表数据
     */
    @Override
    public <R> List<PO> listEqual(SFunction<PO, R> keyExtractor, R value) {
        var wrapper = lambdaQueryWrapper().eq(keyExtractor, value);
        return mapper.selectList(wrapper);
    }

    /**
     * 根据id列表查询数据列表
     *
     * @param idList id列表
     * @return 查询出的数据列表
     */
    @Override
    public List<PO> listByIdList(Collection<Long> idList) {
        if (idList == null || idList.isEmpty()) {
            return new ArrayList<>();
        }
        return mapper.selectBatchIds(idList);
    }

    /**
     * 查询相似的列表数据数量
     *
     * @param keyExtractor 字段信息
     * @param value        值
     * @return 数量
     */
    @Override
    public <R> Long countLike(SFunction<PO, R> keyExtractor, R value) {
        var wrapper = lambdaQueryWrapper().like(keyExtractor, value);
        return mapper.selectCount(wrapper);
    }

    /**
     * 查询相似的列表数据
     *
     * @param keyExtractor 字段信息
     * @param value        值
     * @param <R>          类型
     * @return 列表数据
     */
    @Override
    public <R> List<PO> listLike(SFunction<PO, R> keyExtractor, R value) {
        var wrapper = lambdaQueryWrapper().like(keyExtractor, value);
        return mapper.selectList(wrapper);
    }

    /**
     * 查询列表数据
     *
     * @param wrapper 查询条件
     * @return 列表数据
     */
    @Override
    public List<PO> list(LambdaQueryFunction<PO> wrapper) {
        return mapper.selectList(wrapper.query(new QueryWrapper<PO>().lambda()));
    }

    /**
     * 根据id获取信息
     *
     * @param pk           主键id
     * @param check        是否验证为null
     * @param responseCode 错误消息
     * @param getFunction  获取对象的方法
     * @return 查询出的对象
     */
    @Override
    public VO getById(Long pk, boolean check, BaseResponseCode responseCode, Supplier<PO> getFunction) {
        var po = getPoById(pk, check, responseCode, getFunction);
        return afterGetById(ofVo(po));
    }

    /**
     * 根据id获取信息
     *
     * @param keyExtractor 字段信息
     * @param fk           主键id
     * @param check        是否验证为null
     * @param getFunction  获取对象的方法
     * @param responseCode 错误消息
     * @return 查询出的对象
     */
    @Override
    public <R> VO getById(SFunction<PO, R> keyExtractor, Long fk, boolean check, BaseResponseCode responseCode, Supplier<PO> getFunction) {
        var po = getPoById(keyExtractor, fk, check, responseCode, getFunction);
        return afterGetById(ofVo(po));
    }

    /**
     * 根据id获取信息
     *
     * @param pk           主键id
     * @param check        是否验证为null
     * @param responseCode 错误消息
     * @param getFunction  获取对象的方法
     * @return 查询出的对象
     */
    @Override
    public PO getPoById(Long pk, boolean check, BaseResponseCode responseCode, Supplier<PO> getFunction) {
        var po = mapper.selectById(pk);
        if (getFunction != null && po == null) {
            po = getFunction.get();
        }
        if (check && po == null) {
            var message = errorGetByIdMessage();
            throw new BusinessSimpleException(message == null ? responseCode : message);
        }
        return po;
    }

    /**
     * 根据id获取信息
     *
     * @param keyExtractor 字段信息
     * @param fk           外键id
     * @param check        是否验证为null
     * @param responseCode 错误消息
     * @param getFunction  获取对象的方法
     * @return 查询出的对象
     */
    @Override
    public <R> PO getPoById(SFunction<PO, R> keyExtractor, Long fk, boolean check, BaseResponseCode responseCode, Supplier<PO> getFunction) {
        var wrapper = lambdaQueryWrapper().eq(keyExtractor, fk);
        var po = mapper.selectOne(wrapper);
        if (getFunction != null && po == null) {
            po = getFunction.get();
        }
        if (check && po == null) {
            var message = errorGetByIdMessage();
            throw new BusinessSimpleException(message == null ? responseCode : message);
        }
        return po;
    }

    /**
     * 添加信息
     *
     * @param po 添加对象
     * @return 添加标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<VO> add(PO po) {
        var effectRows = mapper.insert(beforeAdd(po));
        if (effectRows != 1) {
            throw new BusinessSimpleException(BusinessEnum.SAVE_ERROR);
        }
        return new UpdateModel<>(true, afterAdd(ofVo(po)));
    }

    /**
     * 添加信息
     *
     * @param bo 添加对象
     * @return 添加标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<VO> add(BO bo) {
        var po = bo.toPo();
        return add(po);
    }

    /**
     * 添加信息
     *
     * @param boList 添加对象列表
     * @return 添加标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<List<VO>> addBatch(List<BO> boList) {
        var list = boList.stream().map(BO::toPo).toList();
        return addPoBatch(list);
    }

    /**
     * 添加信息
     *
     * @param poList 添加对象列表
     * @return 添加标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<List<VO>> addPoBatch(List<PO> poList) {
        if (poList.isEmpty()) {
            return new UpdateModel<>(true, Collections.emptyList());
        } else if (poList.size() == 1) {
            var updateModel = add(poList.getFirst());
            return new UpdateModel<>(updateModel.isSuccess(), Collections.singletonList(updateModel.getData()));
        } else {
            var effectRows = mapperUtil.batchInsert(mapper, beforeAdd(poList));
            if (effectRows != poList.size()) {
                throw new BusinessSimpleException(BusinessEnum.SAVE_ERROR);
            }
        }
        return new UpdateModel<>(true, Collections.emptyList());
    }

    /**
     * 修改信息
     *
     * @param po 修改对象
     * @return 修改标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<VO> update(PO po) {
        var effectRows = mapper.updateById(beforeUpdate(po));
        if (effectRows != 1) {
            throw new BusinessSimpleException(BusinessEnum.UPDATE_ERROR);
        }
        return new UpdateModel<>(true, afterUpdate(ofVo(po)));
    }

    /**
     * 修改信息
     *
     * @param pk 主键id
     * @param bo 修改对象
     * @return 修改标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<VO> update(Long pk, BO bo) {
        var po = getPoById(pk);
        return update(bo.toPo(po));
    }

    /**
     * 修改信息
     *
     * @param keyExtractor 字段信息
     * @param fk           外键id
     * @param bo           修改对象
     * @return 修改标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public <R> UpdateModel<VO> update(SFunction<PO, R> keyExtractor, Long fk, BO bo) {
        var po = getPoById(keyExtractor, fk);
        return update(bo.toPo(po));
    }

    /**
     * 批量修改信息
     *
     * @param poList 修改对象列表
     * @return 修改标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<VO> updatePoBatch(List<PO> poList) {
        if (poList.size() == 1) {
            return update(poList.get(0));
        }
        mapper.updateByIdBatch(poList);
        return new UpdateModel<>(true);
    }

    /**
     * 删除信息
     *
     * @param pk 主键id
     * @return 删除标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<Boolean> delete(Long pk) {
        mapper.deleteById(beforeDelete(pk));
        return new UpdateModel<>(afterDelete(pk));
    }

    /**
     * 删除信息
     *
     * @param keyExtractor 字段信息
     * @param fk           外键id
     * @return 删除标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public <R> UpdateModel<Boolean> delete(SFunction<PO, R> keyExtractor, Long fk) {
        var wrapper = lambdaQueryWrapper().eq(keyExtractor, fk);
        var list = mapper.selectList(wrapper);
        return deleteByIds(list.stream().map(PO::getId).toList());
    }

    /**
     * 删除信息
     *
     * @param pks 主键列表
     * @return 删除标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public UpdateModel<Boolean> deleteByIds(List<Long> pks) {
        pks.forEach(this::delete);
        return new UpdateModel<>(true);
    }

    /**
     * 删除信息
     *
     * @param keyExtractor 字段信息
     * @param fks          外键列表
     * @return 删除标识
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public <R> UpdateModel<Boolean> delete(SFunction<PO, R> keyExtractor, List<Long> fks) {
        var wrapper = lambdaQueryWrapper().in(keyExtractor, fks);
        var list = mapper.selectList(wrapper);
        return deleteByIds(list.stream().map(PO::getId).toList());
    }

    /**
     * 批量插入数据
     *
     * @param list 数据列表
     * @return 影响行数
     */
    @Override
    public int batchInsert(List<PO> list) {
        if (list == null || list.isEmpty()) {
            return 0;
        }
        return mapperUtil.batchInsert(mapper, list);
    }

    /**
     * 设置项目的开关状态
     * <p>
     * 该状态仅有两种状态 0=关闭 1=开启
     *
     * @param id           主键id
     * @param switchStatus 目标状态状态
     * @param get          获取状态的方法
     * @param set          设置状态的方法
     */
    @Override
    public void updateSwitchStatus(Long id, Integer switchStatus, Function<PO, Integer> get, BiConsumer<PO, Integer> set) {
        var po = getPoById(id);
        if (switchStatus == null) {
            var status = get.apply(po);
            if (status == null) {
                throw new BusinessSimpleException("当前数据没有默认值，请设置默认值后再进行操作");
            }
            set.accept(po, status == 1 ? 0 : 1);
        } else {
            set.accept(po, switchStatus == 1 ? 1 : 0);
        }
        mapper.updateById(po);
    }

    /**
     * 根据id获取内容错误之后的消息提示（优先级大于全局）
     *
     * @return 消息内容
     */
    @Override
    public BaseResponseCode errorGetByIdMessage() {
        var clazz = this.getClass();
        var key = clazz.getName();
        return ERROR_GET_BY_ID_MESSAGE_MAP.computeIfAbsent(key, k -> {
            var flag = clazz.isAnnotationPresent(ServiceInfo.class);
            // 获取当前类上的注解信息
            if (flag) {
                var annotation = this.getClass().getAnnotation(ServiceInfo.class);
                return new BusinessErrorResponseCode("未查询到" + annotation.name());
            }
            return BusinessEnum.NOT_FOUND_OBJECT;
        });
    }

}
